#include <iostream>
#include <sstream>
#include <algorithm>

#include "HumanPicture.h"

using namespace std;

HumanPicture::HumanPicture (const string& pic, const string& cascade) : 
        Picture(pic),
        facesPopulated(false),
        bodiesPopulated(false)
{
    initialized = init(cascade);
}

bool HumanPicture::init(const std::string& cascade) {
    // papa wasn't initialized approperiatly.
    if (Picture::isInitialized() == false) {
        return false;
    }
    
    /* create my own copy of grey scaled version of the 
     * image so i can detect faces on it.
     */
    cv::Mat gray;
    cv::Mat faceImage(cvRound(image.rows), cvRound(image.cols), CV_8UC1);

    /* copy a grey scaled version of original image */
    cv::cvtColor(image, gray, CV_BGR2GRAY);
    /* resize it to some scale */
    cv::resize(gray, faceImage, faceImage.size(), 0, 0, cv::INTER_LINEAR );
    /* perform some sort of histogram transform on the image */
    cv::equalizeHist(faceImage, faceImage);
    
    faceSearchImage = faceImage;

    /* load needed cascades */
    if (!facesCascade.load(cascade)) {
        return false;
    }
    
    return true;
}

bool HumanPicture::populateFaces() {
    if (initialized == false) {
        return false;
    }
    facesCascade.detectMultiScale(faceSearchImage, facesList,
         1.1, 5, 0
        // |cv::CV_HAAR_FIND_BIGGEST_OBJECT
        // |cv::CV_HAAR_DO_ROUGH_SEARCH
        |CV_HAAR_SCALE_IMAGE,
        cv::Size(30, 30) );
    
    // Keep a state, so we don't have to populate everytime.
    facesPopulated = true;
    return true;
}

bool HumanPicture::populateBodies() {
    cout << "IN populateBodies";
    if (initialized == false) {
        return false;
    }
    if (!facesPopulated)
        populateFaces();
    
    // Copy the faces list
    bodiesList = facesList;
    vector<cv::Rect>::iterator it;
    for ( it=bodiesList.begin() ; it < bodiesList.end(); it++ )
    {

        int x        =  max(it->x - it-> width * 2  ,0);
        int width    =  min(it->width * 5, getWidth() - x);
        int y        =  max((int)(it->y - it->height * 0.4), 0);
        int height   =  min(it->height * 10, getHeight() - y);
        
        cout << "Body(" << x << ", " << width << ", " << y << ", " << height << ")\n";
       
        it->x        =  x;
        it->width    =  width;
        it->y        =  y;
        it->height   =  height;
    }
    cout << "END populateBodies";
    bodiesPopulated = true;
    return true;
}

const HumanPicture::Faces& HumanPicture::fetchFaces() {
    if (!facesPopulated) {
        populateFaces();
    }

    // if populateFaces wasn't succefull, this vector will be empty;
    return facesList;
}

const HumanPicture::Faces& HumanPicture::fetchBodies() {
    if (!bodiesPopulated) {
        populateBodies();
    }

    // if populateFaces wasn't succefull, this vector will be empty;
    return bodiesList;
}


void HumanPicture::dumpFaces() {
    if (!facesPopulated) {
        if (!populateFaces()){
            return;
        }
    }
    
    unsigned short i=0;
    for(HumanPicture::Faces::const_iterator r = facesList.begin(); r != facesList.end(); r++, i++ ) {
        ostringstream s;
        s << "File_" << i << ".jpg";
        cout << s.str() << endl;
        cv::imwrite(s.str(), faceSearchImage(*r));
        
        /* TODO: This will work, only if scale is 1. */
        ostringstream s2;
        s2 << "File_Original_" << i << ".jpg";
        cout << s2.str() << endl;
        cv::imwrite(s2.str(), image(*r));
    }
}
